package ar.edu.unq.wannaplay.helpers;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.joda.time.Interval;

import ar.edu.unq.wannaplay.calendareable.Licence;
import ar.edu.unq.wannaplay.employee.Employee;
import ar.edu.unq.wannaplay.entity.Entity;
import ar.edu.unq.wannaplay.exceptions.EmployeeCantTakeLicenceException;
import ar.edu.unq.wannaplay.exceptions.NegativeNumberException;
import ar.edu.unq.wannaplay.utils.Pair;

/**
 * Creates an contains the information about licences
 */
public class LicenceSpitter extends Entity {
    private static final long serialVersionUID = 1L;

    /**
     * The first int from the pair is the anualMax and the second is the
     * consecutive max that any employee can take over a year.
     */
    private Map<String, Pair<Integer, Integer>> licencesInformation = new HashMap<String, Pair<Integer, Integer>>();

    /**
     * Adds a type of licence
     */
    public void addLicence(final String newLicence, final int anualMax, final int consecutiveMax)
            throws NegativeNumberException {
        if (anualMax < 0 || consecutiveMax < 0) {
            throw new NegativeNumberException("You can't use negative numbers here");
        } else {
            this.getLicencesInformation().put(newLicence, new Pair<Integer, Integer>(anualMax, consecutiveMax));
        }
    }

    /**
     * Removes a type of licence
     */
    public void removeLicence(final String toRemove) {
        this.getLicencesInformation().remove(toRemove);
    }

    /**
     * Modifies the anual maximum of times that a licence can be taken
     */
    public void modifyAnualMaximum(final String aLicence, final int newAnualMax) {
        this.getLicencesInformation().get(aLicence).setFirst(newAnualMax);
    }

    /**
     * Returns the anual maximum of times that a licence can be taken
     */
    public int getAnualMaximumFrom(final String aLicence) {
        return this.getLicencesInformation().get(aLicence).getFirst();
    }

    /**
     * Modifies the number of consecutive times a licence can be taken
     */
    public void modifyConsecutiveMaximum(final String aLicence, final int newConsecutiveMax) {
        this.getLicencesInformation().get(aLicence).setSecond(newConsecutiveMax);
    }

    /**
     * Returns the maximum of consecutive times that a licence can be taken
     */
    public int getConsecutiveMaximumFrom(final String aLicence) {
        return this.getLicencesInformation().get(aLicence).getSecond();
    }

    /**
     * Gives a licence to an employee. In case that an employee has taken too
     * many licences, it throws an EmployeeCantTakeLicenceException
     */
    public void giveLicenceTo(final String aLicence, final Employee anEmployee, final Interval anInterval)
            throws EmployeeCantTakeLicenceException {
        if (this.canEmployeeTakeLicence(anEmployee, aLicence, anInterval)) {
            anEmployee.addLicence(new Licence(aLicence, anInterval));
        } else {
            throw new EmployeeCantTakeLicenceException("Employee cant take the licence");
        }
    }

    public boolean canEmployeeTakeLicence(final Employee anEmployee, final String aLicence, final Interval anInterval) {
        List<Licence> licencesTaken = anEmployee.getLicensesByYear(anInterval.getStart().getYear());
        return this.canEmployeeTakeAnotherLicenceThisYear(aLicence, licencesTaken, anInterval)
                && this.canEmployeeTakeAnotherConsecutiveLicence(aLicence, licencesTaken, anInterval);
    }

    private boolean canEmployeeTakeAnotherConsecutiveLicence(final String aLicence, final List<Licence> licencesTaken,
            final Interval anInterval) {
        for (Licence currentLicence : licencesTaken) {
            if (currentLicence.getName().equals(aLicence)
                    && this.isConsecutiveInterval(anInterval, currentLicence.getInterval())) {
                Interval intervalsAppend = anInterval.getStart().isAfter(currentLicence.getInterval().getStart()) ? this
                        .createNewInterval2(anInterval, currentLicence) : this.createNewInterval(anInterval,
                        currentLicence);
                if (intervalsAppend.getEnd().minusDays(intervalsAppend.getStart().getDayOfYear()).getDayOfYear() > this
                        .getConsecutiveMaximumFrom(aLicence)) {
                    return false;
                }
                return this.canEmployeeTakeAnotherConsecutiveLicence(aLicence, licencesTaken, intervalsAppend);
            }
        }
        return true;
    }

    private Interval createNewInterval2(final Interval anInterval, final Licence currentLicence) {
        return new Interval(currentLicence.getInterval().getStart(), anInterval.getEnd());
    }

    private Interval createNewInterval(final Interval anInterval, final Licence currentLicence) {
        return new Interval(anInterval.getStart(), currentLicence.getInterval().getEnd());
    }

    private boolean isConsecutiveInterval(final Interval anInterval, final Interval anotherInterval) {
        return anInterval.getStart().equals(anotherInterval.getEnd())
                || anInterval.getEnd().equals(anotherInterval.getStart());
    }

    private boolean canEmployeeTakeAnotherLicenceThisYear(final String aLicence, final List<Licence> licencesTaken,
            final Interval anInterval) {
        int timesTaken = 0;
        int daysWanted = anInterval.getEnd().minusDays(anInterval.getStart().getDayOfYear()).getDayOfYear();
        for (Licence currentLicence : licencesTaken) {
            if (currentLicence.getName().equals(aLicence)) {
                timesTaken += currentLicence.getInterval().getEnd()
                        .minusDays(currentLicence.getInterval().getStart().getDayOfYear()).getDayOfYear();
            }
        }
        return timesTaken + daysWanted <= this.getAnualMaximumFrom(aLicence);
    }

    /**
     * Resets all information
     */
    public void deleteData() {
        this.setLicencesInformation(new HashMap<String, Pair<Integer, Integer>>());
    }

    // setters & getters
    public Map<String, Pair<Integer, Integer>> getLicencesInformation() {
        return licencesInformation;
    }

    public void setLicencesInformation(final Map<String, Pair<Integer, Integer>> licencesInformation) {
        this.licencesInformation = licencesInformation;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || !(obj instanceof LicenceSpitter)) {
            return false;
        }

        LicenceSpitter other = (LicenceSpitter) obj;

        if (this.getId() == other.getId()) {
            return true;
        }
        if ((Integer) this.getId() == null) {
            return false;
        }

        // equivalence by id
        return ((Integer) this.getId()).equals(other.getId());
    }

    @Override
    public int hashCode() {
        if ((Integer) this.getId() != null) {
            return ((Integer) this.getId()).hashCode();
        } else {
            return super.hashCode();
        }
    }
}
